    // Copyright (C) 2005  Davis E. King (davis@dlib.net), Keita Mochizuki
    // License: Boost Software License   See LICENSE.txt for the full license.
    #undef DLIB_GUI_CORE_KERNEl_ABSTRACT_
    #ifdef DLIB_GUI_CORE_KERNEl_ABSTRACT_

    #include <string>
    #include "../algs.h"
    #include "../geometry/rectangle_abstract.h"
    #include "../unicode/unicode_abstract.h"

    namespace dlib
    {

        /*!
            OVERVIEW:
                This is a set of objects and functions which provide a very basic
                framework for manipulating windows.  It is intended to provide a
                portable interface which can be used to build a more complex windowing
                toolkit.

            EXCEPTIONS
                Do not let an exception leave any of the base_window event handlers.
                The results of doing so are undefined.

            THREAD SAFETY
                Event Handlers
                    All event handlers are executed in a special event handling thread.
                    This means that you must not do anything that will take a long time or
                    block while in an event handler.  Doing so will freeze all event
                    processing.

                    Also, don't rely on get_thread_id() always returning the same ID from
                    inside event handlers.

                canvas
                    Never access a canvas object outside of the paint() callback
                    that supplied it.  Only access a canvas object from the event
                    handling thread.  After the paint() event handler has returned do
                    not access that canvas object again.

                base_window
                    All methods for this class are thread safe.  You may call them
                    from any thread and do not need to serialize access.
        !*/

    // ----------------------------------------------------------------------------------------

        void put_on_clipboard (
            const std::string& str
        );
        /*!
            ensures
                - posts the contents of str to the system clipboard
            throws
                - std::bad_alloc
                - dlib::gui_error
                - dlib::thread_error
        !*/

        // overloads for wide character strings
        void put_on_clipboard (const std::wstring& str);
        void put_on_clipboard (const dlib::ustring& str);

    // ----------------------------------------------------------------------------------------

        void get_from_clipboard (
            std::string& str
        );
        /*!
            ensures
                - if (there is string data on the system clipboard) then
                    - #str == the data from the clipboard
                - else
                    - #str == ""
            throws
                - std::bad_alloc
                - dlib::gui_error
                - dlib::thread_error
        !*/

        // overloads for wide character strings
        void get_from_clipboard (std::wtring& str);
        void get_from_clipboard (dlib::utring& str);

    // ----------------------------------------------------------------------------------------

        class canvas : public rectangle
        {
            /*!
                POINTERS AND REFERENCES TO INTERNAL DATA
                    All functions of this object may invalidate pointers and references
                    to internal data.

                INITIAL VALUE
                    The initial value of each pixel is undefined.
                    is_empty() == false

                WHAT THIS OBJECT REPRESENTS
                    This object represents a rectangular area of a window that you
                    can draw on.

                    Each pixel can be accessed with the following syntax:
                        canvas_instance[y][x].red   == the red value for this pixel
                        canvas_instance[y][x].blue  == the blue value for this pixel
                        canvas_instance[y][x].green == the green value for this pixel

                    The origin, i.e. (0,0), of the x,y coordinate plane of the canvas is in
                    the upper left corner of the canvas.  Note that the upper left corner
                    of the canvas appears at the point (left(),top()) in its window.
            !*/

        public:

            struct pixel
            {
                /*!
                    WHAT THIS OBJECT REPRESENTS
                        This object represents a single pixel.  Each pixel's value
                        ranges from 0 to 255 with 0 indicating that the color is not
                        present in the pixel at all and 255 indicating that the color
                        is present in the pixel with maximum intensity.

                        Note that the structure, order, and size of this struct are
                        implementation dependent.  It will always contain fields called
                        red, green, and blue but they may not be in that order and there
                        may be padding.

                        Also note that pixel_traits<> is defined for this pixel type,
                        thus you can use it in assign_pixel() calls.
                !*/
                unsigned char red;
                unsigned char green;
                unsigned char blue;
            };

            pixel* operator[] (
                unsigned long row
            ) const;
            /*!
                requires
                    - row < height()
                ensures
                    - returns an array of width() pixel structs that represents the given
                      row of pixels in the canvas.
            !*/

            void fill (
                unsigned char red,
                unsigned char green,
                unsigned char blue
            ) const;
            /*!
                ensures
                    - for all valid values of x and y:
                        - (#*this)[y][x].red = red
                        - (#*this)[y][x].green = green
                        - (#*this)[y][x].blue = blue
            !*/

        private:

            // restricted functions
            canvas();        // normal constructor
            canvas(canvas&);        // copy constructor
            canvas& operator=(canvas&);    // assignment operator
        };

    // ----------------------------------------------------------------------------------------

        class base_window
        {

            /*!
                WHAT THIS OBJECT REPRESENTS
                    This object represents a window on the desktop.  A window has a "client
                    area" that is a region of the screen that you can draw whatever you like
                    on.  You implement the paint() callback and use the canvas object to do
                    this drawing.

                INITIAL STATE
                    - The initial state of the window is to be hidden.  This means you need
                      to call show() to make it appear.
                    - is_closed() == false

                paint() callback:
                    This is where you will do all your drawing.  It is triggered when
                    part of the window needs to be drawn/redrawn.

                mouse events:
                    It is important to note a few things about the mouse events.  First,
                    the on_mouse_move() event is not triggered for each pixel the mouse crosses
                    but rather its frequency and precision is implementation dependent.

                    Second, it is possible that a mouse button may be depressed but the
                    corresponding button release event does not go to the window.  For instance,
                    if the mouse is outside the window and some other application jumps to the
                    top it is possible that the new application will receive any mouse button
                    release events rather than the original window.  But the point is that
                    you should not rely on always getting a button up event for every button
                    down event.

                keydown event:
                    Note that the existence of a typematic action (holding down a key
                    and having it start to repeat itself after a moment) for each key is
                    totally implementation dependent.  So don't rely on it for any key
                    and conversely don't assume it isn't present either.

                The base_window::wm mutex
                    This is a reference to a global rmutex.  All instances of base_window make
                    reference to the same global rmutex.  It is used to synchronize access to
                    the base_window to make it thread safe.  It is also always locked before
                    an event handler is called.
            !*/

        public:

            enum on_close_return_code
            {
                DO_NOT_CLOSE_WINDOW,
                CLOSE_WINDOW
            };

            enum mouse_state_masks
            {
                /*!
                    These constants represent the various buttons referenced by
                    mouse events.
                !*/
                NONE = 0,
                LEFT = 1,
                RIGHT = 2,
                MIDDLE = 4,
                SHIFT = 8,
                CONTROL = 16
            };

            enum keyboard_state_masks
            {
                /*!
                    These constants represent the various modifier buttons that
                    could be in effect during a key press on the keyboard
                !*/
                KBD_MOD_NONE = 0,
                KBD_MOD_SHIFT = 1,
                KBD_MOD_CONTROL = 2,
                KBD_MOD_ALT = 4,
                KBD_MOD_META = 8,
                KBD_MOD_CAPS_LOCK = 16,
                KBD_MOD_NUM_LOCK = 32,
                KBD_MOD_SCROLL_LOCK = 64
            };

            enum non_printable_keyboard_keys
            {
                KEY_BACKSPACE,
                KEY_SHIFT,
                KEY_CTRL,
                KEY_ALT,
                KEY_PAUSE,
                KEY_CAPS_LOCK,
                KEY_ESC,
                KEY_PAGE_UP,
                KEY_PAGE_DOWN,
                KEY_END,
                KEY_HOME,
                KEY_LEFT,           // This is the left arrow key
                KEY_RIGHT,          // This is the right arrow key
                KEY_UP,             // This is the up arrow key
                KEY_DOWN,           // This is the down arrow key
                KEY_INSERT,
                KEY_DELETE,
                KEY_SCROLL_LOCK,

                // Function Keys
                KEY_F1,
                KEY_F2,
                KEY_F3,
                KEY_F4,
                KEY_F5,
                KEY_F6,
                KEY_F7,
                KEY_F8,
                KEY_F9,
                KEY_F10,
                KEY_F11,
                KEY_F12
            };

            base_window (
                bool resizable = true,
                bool undecorated = false
            );
            /*!
                requires
                    - if (undecorated == true) then
                        - resizable == false
                ensures
                    - #*this has been properly initialized
                    - if (resizable == true) then
                        - this window will be resizable by the user
                    - else
                        - this window will not be resizable by the user
                    - if (undecorated == true) then
                        - this window will not have any graphical elements outside
                          of its drawable area or appear in the system task bar. It
                          also won't take the input focus from other windows.
                          (it is suitable for making things such as popup menus)
                throws
                    - std::bad_alloc
                    - dlib::thread_error
                    - dlib::gui_error
                        This exception is thrown if there is an error while
                        creating this window.
            !*/

            virtual ~base_window (
            );
            /*!
                ensures
                    - does NOT trigger the on_window_close() event
                    - all resources associated with *this have been released
                    - closes this window
            !*/

            void close_window (
            );
            /*!
                ensures
                    - #is_closed() == true
                      (i.e. permanently closes this window.  The window is removed from the
                      screen and no more events will be dispatched to this window. )
                    - does NOT trigger the on_window_close() event
            !*/

            void wait_until_closed (
            ) const;
            /*!
                ensures
                    - blocks until is_closed() == true
            !*/

            bool is_closed (
            ) const;
            /*!
                ensures
                    - returns true if this window has been closed, false otherwise.
                      (Note that closed windows do not receive any callbacks at all.
                       They are also not visible on the screen.)
            !*/

            void set_title (
                const std::string& title
            );
            /*!
                ensures
                    - if (is_closed() == false) then
                        - sets the title of the window
            !*/

            void set_title (
                const std::wstring& title
            );
            /*!
                ensures
                    - if (is_closed() == false) then
                        - sets the title of the window
            !*/

            void set_title (
                const dlib::ustring& title
            );
            /*!
                ensures
                    - if (is_closed() == false) then
                        - sets the title of the window
            !*/

            virtual void show (
            );
            /*!
                ensures
                    - if (is_closed() == false) then
                        - this window will appear on the screen
            !*/

            virtual void hide(
            );
            /*!
                ensures
                    - if (is_closed() == false) then
                        - the window does not appear on the screen
            !*/

            void set_size (
                int width,
                int height
            );
            /*!
                ensures
                    - if (is_closed() == false) then
                        - The width of the client area of this window is at least width
                          pixels.
                        - The height of the client area of this window is at least height
                          pixels.
                        - if (the window wasn't already this size) then
                            - triggers the on_window_resized() callback
            !*/

            void set_pos (
                long x,
                long y
            );
            /*!
                ensures
                    - if (is_closed() == false) then
                        - sets the upper left corner of this window to the position (x,y)
                          on the desktop.  Note that the origin (0,0) is at the upper left
                          corner of the desktop.
            !*/

            void get_pos (
                long& x,
                long& y
            ) const;
            /*!
                ensures
                    - if (is_closed() == false) then
                        - #x == the x coordinate of the upper left corner of the client area of
                          this window.
                        - #y == the y coordinate of the upper left corner of the client area of
                          this window.
                        - i.e. the point (#x,#y) on the desktop is coincident with the point
                          (0,0) in the client area of this window.
                    - else
                        - #x == 0
                        - #y == 0
            !*/

            void get_size (
                unsigned long& width,
                unsigned long& height
            ) const;
            /*!
                ensures
                    - if (is_closed() == false) then
                        - #width == the width of the client area of this window in pixels
                        - #height == the height of the client area of this window in pixels
                    - else
                        - #width == 0
                        - #height == 0
            !*/

            void get_display_size (
                unsigned long& width,
                unsigned long& height
            ) const;
            /*!
                ensures
                    - if (is_closed() == false) then
                        - #width == the width in pixels of the display device that contains this window
                        - #height == the height in pixels of the display device that contains this window
                    - else
                        - #width == 0
                        - #height == 0
            !*/

            void invalidate_rectangle (
                const rectangle& rect
            );
            /*!
                ensures
                    - if (is_closed() == false) then
                        - causes the area of this window defined by rect to become invalid.
                          This means that a paint() message will be dispatched to repaint
                          this area of the window.  Note that it is possible that the
                          resulting paint() message may include a bigger rectangle than
                          the one defined by rect.
            !*/

            void trigger_user_event (
                void* p,
                int i
            );
            /*!
                ensures
                    - will never block (even if some other thread has a lock on the
                      global mutex referenced by wm.)
                    - if (is_closed() == false) then
                        - causes the on_user_event() event to be called with
                          the given arguments.
            !*/

            void set_im_pos (
                long x_,
                long y_
            );
            /*!
                ensures
                    - if (is_closed() == false) then
                        - sets the left-top position of input method rectangle used
                          for wide character input methods.
            !*/

        protected:
            const rmutex& wm;

            // let the window close by default
            virtual on_close_return_code on_window_close(
            ){return CLOSE_WINDOW;}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called when the user attempts to close this window
                    - if (this function returns CLOSE_WINDOW) then
                        - #is_closed() == true  (i.e. this window will be closed)
                        - it is safe to call "delete this;" inside on_window_close()
                          if *this was allocated on the heap and no one will try to
                          access *this anymore.
                    - else
                        - this window will not be closed and the attempt to close it
                          by the user will have no effect.
                        - #is_closed() == false
                ensures
                    - does not change the state of mutex wm
            !*/

            // do nothing by default
            virtual void on_user_event (
                void* p,
                int i
            ){}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called whenever someone calls trigger_user_event()
                ensures
                    - does not change the state of mutex wm
            !*/

            // do nothing by default
            virtual void on_window_resized(
            ){}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called when this window is resized
                ensures
                    - does not change the state of mutex wm
            !*/

            // do nothing by default
            virtual void on_window_moved(
            ){}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called when this window's position changes
                ensures
                    - does not change the state of mutex wm
            !*/

            // do nothing by default
            virtual void on_mouse_down (
                unsigned long btn,
                unsigned long state,
                long x,
                long y,
                bool is_double_click
            ){}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called when the user depresses one of the mouse buttons
                    - btn == the button that was depressed. (either LEFT, MIDDLE, or RIGHT)
                    - state == the bitwise OR of the buttons that are currently depressed
                      excluding the button given by btn. (from the mouse_state_masks enum)
                    - (x,y) == the position of the mouse (relative to the upper left corner
                      of the window) when this event occurred.  Note that the mouse may be
                      outside the window.
                    - if (this is the second button press of a double click) then
                        - is_double_click == true
                    - else
                        - is_double_click == false
                ensures
                    - does not change the state of mutex wm
            !*/

            // do nothing by default
            virtual void on_mouse_up (
                unsigned long btn,
                unsigned long state,
                long x,
                long y
            ){}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called when the user releases one of the mouse buttons
                    - btn == the button that was released. (either LEFT, MIDDLE, or RIGHT)
                    - state == the bitwise OR of the buttons that are currently depressed
                      (from the mouse_state_masks enum)
                    - (x,y) == the position of the mouse (relative to the upper left corner
                      of the window) when this event occurred.  Note that the mouse may be
                      outside the window.
                ensures
                    - does not change the state of mutex wm
            !*/

            // do nothing by default
            virtual void on_mouse_move (
                unsigned long state,
                long x,
                long y
            ){}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called when the user moves the mouse
                    - state == the bitwise OR of the buttons that are currently depressed
                      (from the mouse_state_masks enum)
                    - (x,y) == the position of the mouse (relative to the upper left corner
                      of the window) when this event occurred.
                    - if (the user is holding down one or more of the mouse buttons) then
                        - the mouse move events will continue to track the mouse even if
                          it goes out of the window.  This will continue until the user
                          releases all the mouse buttons.
                ensures
                    - does not change the state of mutex wm
            !*/

            // do nothing by default
            virtual void on_mouse_leave (
            ){}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called when the mouse leaves this window
                ensures
                    - does not change the state of mutex wm
            !*/

            // do nothing by default
            virtual void on_mouse_enter (
            ){}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called when the mouse enters this window
                ensures
                    - does not change the state of mutex wm
            !*/

            // do nothing by default
            virtual void on_focus_gained (
            ){}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called when this window gains input focus
                ensures
                    - does not change the state of mutex wm
            !*/

            // do nothing by default
            virtual void on_focus_lost (
            ){}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called when this window loses input focus
                ensures
                    - does not change the state of mutex wm
            !*/

            // do nothing by default
            virtual void on_wheel_up (
                unsigned long state
            ){}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called every time the mouse wheel is scrolled up one notch
                    - state == the bitwise OR of the buttons that are currently depressed
                      (from the mouse_state_masks enum)
                ensures
                    - does not change the state of mutex wm
            !*/

            // do nothing by default
            virtual void on_wheel_down (
                unsigned long state
            ){}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called every time the mouse wheel is scrolled down one notch
                    - state == the bitwise OR of the buttons that are currently depressed
                      (from the mouse_state_masks enum)
                ensures
                    - does not change the state of mutex wm
            !*/

            // do nothing by default
            virtual void on_keydown (
                unsigned long key,
                bool is_printable,
                unsigned long state
            ){}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called when a keyboard key is pressed or if a key is held
                      down then this is called repeatedly at a certain rate once the
                      typematic action begins (note that some keys might not have any
                      typematic action on some platforms).
                    - if (is_printable) then
                        - key == the character that was pressed. (e.g. 'a', 'b', '1' etc.)
                        - this is a printable character.  Note that ' ', '\t', and
                          '\n' (this is the return/enter key) are all considered printable.
                    - else
                        - key == one of the non_printable_keyboard_keys enums.
                    - state == the bitwise OR of the keyboard modifiers that are currently
                      depressed (taken from keyboard_state_masks).
                    - if (key is not in the range 'a' to 'z' or 'A' to 'Z') then
                        - if (the shift key was down when this key was pressed) then
                            - (state & KBD_MOD_SHIFT) != 0
                        - else
                            - (state & KBD_MOD_SHIFT) == 0
                    - else
                        - the state of the shift key is implementation defined
                ensures
                    - does not change the state of mutex wm
            !*/

            virtual void on_string_put (
                const std::wstring &str
            ){}
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called when a wide/multibyte character input method determines a string
                      that is being input to the window.
                    - str == the string that is being input
                ensures
                    - does not change the state of mutex wm
            !*/

        private:

            virtual void paint (
                const canvas& c
            ) =0;
            /*!
                requires
                    - is_closed() == false
                    - mutex wm is locked
                    - is called when part of the window needs to be repainted for
                      any reason.
                    - c == a canvas object that represents the invalid area of this
                      window which needs to be painted.
                ensures
                    - does not change the state of mutex wm
            !*/

            base_window(base_window&);        // copy constructor
            base_window& operator=(base_window&);    // assignment operator

        };

    // ----------------------------------------------------------------------------------------

    }

    #endif // DLIB_GUI_CORE_KERNEl_ABSTRACT_